import { EConnectState } from "./EConnectState";
import MonoHelper, { EActionType } from "../../LTGame/Utils/MonoHelper";
import { EMessageC2S } from "../../common/EMessageC2S";
import { WrapRegistData } from "../../common/CommonTransData";
import { EClientType } from "../../common/EClientType";
import { EMessageS2C } from "../../common/EMessageS2C";
import { TransPack } from "../../server/framework/common/TransPack";
import { EMessageC2C } from "../../common/EMessageC2C";
import { MsgPack } from "../../LTGame/3rd/msgpack/MsgPack";
import { PakoEx } from "../../LTGame/3rd/pako/PakoEx";

const TAG = '[DebugServerConnection]';
const MAX_RECONNECT_COUNT = 3;

/**
 * 用于连接后台
 */
export class DebugServerConnection {

    private _ws: WebSocket;

    private _cacheAddress: string;

    public state: EConnectState;

    private _mapEvent: Map<number, Laya.Handler>;
    private _clientMapEvent: Map<number, Laya.Handler>;
    private _reconnectCount: number;
    private _nextReconnectTick: number;
    private _lastHeartTick: number;

    private _cacheHeartPack: Uint8Array;

    constructor(serverAddress: string) {
        this._cacheAddress = serverAddress;
        this._mapEvent = new Map<number, Laya.Handler>();
        this._clientMapEvent = new Map<number, Laya.Handler>();
        this.state = EConnectState.NoConnect;
        this._reconnectCount = 0;
        this._lastHeartTick = Date.now();
        MonoHelper.instance.AddAction(EActionType.Update, this, this._LogicUpdate);

        this.RegistMsg(EMessageS2C.TransToOther, this, this._HandleTransOtherPack);

        this._InitHeartBeat();
    }

    private _InitHeartBeat() {
        let transPack = new TransPack();
        transPack.type = EMessageC2S.Heart;
        let uncompressData = MsgPack.Encode(transPack);
        this._cacheHeartPack = PakoEx.Compress(uncompressData);
    }

    private _HandleTransOtherPack(pack: TransPack) {
        if (this._clientMapEvent.has(pack.type)) {
            let getHandle = this._clientMapEvent.get(pack.type);
            getHandle.runWith([pack]);
            if (getHandle.once) {
                this._clientMapEvent.delete(pack.type);
            }
        } else {
            if (pack.type != EMessageC2C.CommonOperate_Back) {
                console.error(TAG, "未处理的消息id:", pack.type, pack);
            }
        }
    }

    public Connect() {
        if (this.state != EConnectState.NoConnect
            && this.state != EConnectState.DisConnected) {
            console.error(TAG, "链接状态错误:", this.state);
            return;
        }
        if (WebSocket == null) {
            console.error(TAG, "当前环境不支持websocket");
            this.state = EConnectState.Error;
            return;
        }
        this.state = EConnectState.Connecting;
        this._ws = new WebSocket(this._cacheAddress, 'echo-protocol');
        this._ws.onopen = (socket) => {
            console.log(TAG, "连接成功");
            this._reconnectCount = 0;
            this.state = EConnectState.Connected;
            this._lastHeartTick = Date.now();
            let sendData = {} as FakeMsgPack;
            sendData.type = EMessageC2S.Regist;
            let fakeData = new WrapRegistData();
            fakeData.registType = EClientType.Debugger;
            fakeData.clientName = 'layaDebugger';
            sendData.data = fakeData;

            let sendBin = MsgPack.Encode(sendData);
            let compressedData = PakoEx.Compress(sendBin);
            this._ws.send(compressedData);
        }
        this._ws.onmessage = (msg: MessageEvent) => {
            this._HandleRecvMsg(msg);
        }
        this._ws.onclose = (msg) => {
            console.log(TAG, "链接已断开", msg);
            this._reconnectCount++;
            if (this._reconnectCount > MAX_RECONNECT_COUNT) {
                this.state = EConnectState.Error;
            } else {
                this.state = EConnectState.DisConnected;
                this._nextReconnectTick = Date.now() + 1000;
            }
        }
    }

    private _LogicUpdate(dt: number) {
        switch (this.state) {
            case EConnectState.Connected:
                this._HandleHeartBeat();
                break;
            case EConnectState.DisConnected:
                if (Date.now() > this._nextReconnectTick) {
                    this.Connect();
                }
                break;
        }
    }

    private _HandleHeartBeat() {
        let nowTime = Date.now();
        let passTick = nowTime - this._lastHeartTick;
        // 每30秒发一次心跳
        if (passTick > 30 * 1000) {
            this._ws.send(this._cacheHeartPack);
            this._lastHeartTick = nowTime;
        }
    }

    private async _HandleRecvMsg(msg: MessageEvent) {
        let blobData = msg.data as Blob;
        let ab = await blobData.arrayBuffer();
        let uint8Array = new Uint8Array(ab);
        let uncompressedData = PakoEx.UnCompress(uint8Array);
        let parseData = MsgPack.Decode(uncompressedData) as FakeMsgPack;
        if (parseData.type == null) {
            console.error(TAG, "接收到无效消息:", msg);
            return;
        }
        if (this._mapEvent.has(parseData.type)) {
            let getHandle = this._mapEvent.get(parseData.type);
            getHandle.runWith([parseData.data]);
            if (getHandle.once) {
                this._mapEvent.delete(parseData.type);
            }
        } else {
            console.error(TAG, "未处理的消息id:", parseData.type, msg);
        }
    }

    public RegistClientMsg(msgId: number, caller: any, act: Function, once: boolean = false) {
        if (this._clientMapEvent.has(msgId)) {
            if (msgId != EMessageC2C.CommonOperate_Back) {
                console.error(TAG, "不能重复注册msgId", msgId);
            }
            return;
        }
        let handle = Laya.Handler.create(caller, act, null, once);
        this._clientMapEvent.set(msgId, handle);
    }

    public UnRegistClientMsg(msgId: number) {
        if (!this._clientMapEvent.has(msgId)) {
            return;
        }
        let handle = this._clientMapEvent.get(msgId);
        handle.recover();
        this._clientMapEvent.delete(msgId);
    }

    public RegistMsg(msgId: number, caller: any, act: Function, once: boolean = false) {
        if (this._mapEvent.has(msgId)) {
            console.error(TAG, "不能重复注册msgId", msgId);
            return;
        }
        let handle = Laya.Handler.create(caller, act, null, once);
        this._mapEvent.set(msgId, handle);
    }

    public UnRegistMsg(msgId: number) {
        if (!this._mapEvent.has(msgId)) {
            return;
        }
        let handle = this._mapEvent.get(msgId);
        handle.recover();
        this._mapEvent.delete(msgId);
    }

    public Send(msgId: number, data: any) {
        if (this.state != EConnectState.Connected) {
            console.error(TAG, "状态错误:", this.state);
            return;
        }
        let wrapPack = new FakeMsgPack();
        wrapPack.type = msgId;
        if (data != null) {
            wrapPack.data = data;
        }
        // let sendJson = JSON.stringify(wrapPack);
        let sendBin = MsgPack.Encode(wrapPack);
        let compressedData = PakoEx.Compress(sendBin);
        this._ws.send(compressedData);
    }

}

class FakeMsgPack {
    type: number;
    data: any;
}